<template>
  <div class="box-border w-full h-full pb-10 flex flex-col justify-center items-center">
    <h1 class="text-3xl font-medium mb-6">WebSocket 播放器</h1>
    <div ref="videoElementRef" class="canvas-container w-[1000px] h-[500px]">
      <canvas id="canvas" class="w-full h-full bg-black object-contain mt-5 border border-gray-300"></canvas>
    </div>
  </div>
</template>

<script setup>
import { ref, onMounted, onBeforeUnmount } from 'vue'
import { loadPlayer } from 'rtsp-relay/browser'
import { message } from 'ant-design-vue'
import { fullScreen } from '@/utils'
import {
  BASE_URL,
  wsProtocol,
  protocol,
  host,
  hostname,
  webRtcStreamerPort,
  streamRelayWsPort
} from '@/utils/request/config'
import { login, getSessionRuntimeInfo, getServerInfo } from '@/api/common'
import MouseEventHandler from '@/utils/mouse-event-handler'

let sessionId = 0
let uid = ''
const autoLogin = async () => {
  const encodedPassword = btoa('ids1@3') // 将密码转换为 Base64 格式
  const response = await login('admin', encodedPassword)
  uid = response.data.uid // 从登录响应中获取用户的唯一标识符 uid

  fetchServerInfo()

  connect()
}
autoLogin()

let rtspUsername = ''
let rtspPassword = ''
// 获取系统信息
const fetchServerInfo = async () => {
  try {
    const res = await getServerInfo(uid)
    rtspUsername = res?.data?.rtsp?.rtsp_username
    rtspPassword = res?.data?.rtsp?.rtsp_password
  } catch (error) {
    console.log('error: ', error)
  }
}

// 获取运行时信息
const fetchServerRuntimeInfo = async (config) => {
  try {
    const res = await getSessionRuntimeInfo(sessionId, uid)
    return res?.data?.outputs || {}
  } catch (error) {
    console.log('error: ', error)
  }
}

let allowReconnect = true
let eventSocket = null
let eventRetryTimer = null
let reconnectAttempts = 0
const maxReconnectAttempts = 50 // 限制最大重连次数

const eventSocketUrl = `${wsProtocol}//${host}/ws/mouse/event`
const eventSocketConnect = (socketUrl) => {
  eventSocket = new WebSocket(socketUrl)

  eventSocket.onopen = () => {
    if (eventRetryTimer) {
      clearTimeout(eventRetryTimer)
      eventRetryTimer = null
    }
    console.log('Event webSocket connection')
  }

  eventSocket.onclose = async () => {
    reconnection()
    console.log('Event webSocket connection closed')
  }

  eventSocket.onerror = (error) => {
    reconnection()
    console.error('Event webSocket error observed:', error)
  }

  function reconnection() {
    if (allowReconnect && reconnectAttempts < maxReconnectAttempts) {
      eventRetryTimer = setTimeout(() => {
        reconnectAttempts++
        eventSocketConnect(eventSocketUrl)
      }, 5000)
    } else if (reconnectAttempts >= maxReconnectAttempts) {
      console.log('Reached maximum reconnection attempts. Stopping retries.')
    }
  }
}
eventSocketConnect(eventSocketUrl)

// 发送消息到 WebSocket
const sendWebSocketMessage = (params) => {
  if (eventSocket?.readyState === WebSocket.OPEN) {
    const queryString = new URLSearchParams(params).toString()
    eventSocket.send(queryString)
  }
}

// 事件回调
const handleEvent = (eventType, additionalParams) => {
  if (eventType === 'wheel') eventType = 'whell'

  const { zoom, xOffset, yOffset, startX, endX, startY, endY, playerWidth, playerHeight } = additionalParams

  const params = {
    uid,
    session_id: sessionId,
    type: eventType,
    zoom: zoom,
    x_offset: xOffset,
    y_offset: yOffset,
    s_x: startX,
    e_x: endX,
    s_y: startY,
    e_y: endY,
    win_width: playerWidth,
    win_height: playerHeight
  }

  sendWebSocketMessage(params)
}

// 绑定鼠标事件
const bindMouseEvents = (element, config) => {
  const mouseHandler = new MouseEventHandler(element, config)

  element.addEventListener('contextmenu', (e) => e.preventDefault()) // 禁止右键菜单

  return mouseHandler
}

const hidePlayer = (canvasId) => {
  const canvasElement = document.getElementById(canvasId)
  if (canvasElement) {
    canvasElement.style.opacity = '0.25'
  }
}

const showPlayer = (canvasId) => {
  const canvasElement = document.getElementById(canvasId)
  if (canvasElement) {
    canvasElement.style.opacity = '1'
  }
}

let player = null
const connect = async () => {
  const response = await fetchServerRuntimeInfo()
  const {
    fps,
    pano_width,
    pano_height,
    rect_width,
    rect_height,
    pano_transcoding_quality,
    rect_transcoding_quality,
    urls
  } = response

  let panoramaStreamSuffix = ''

  if (Array.isArray(urls) && urls.length >= 2 && (urls[0] || urls[1])) {
    panoramaStreamSuffix = urls[0]
     message.success('Successfully got suffix of stream address')
  } else {
    console.log('Failed to get suffix of stream address: No valid stream address found.')
    message.info('Failed to get suffix of stream address: No valid stream address found.')
  }

  const baseAdditionalFlags = ['-f', 'mpegts', '-codec:v', 'mpeg1video', '-r', fps || 25]

  const additionalFlags = [
    '-q',
    pano_transcoding_quality || 6,
    '-s',
    `${pano_width || 1920}x${pano_height || 1080}`,
    ...baseAdditionalFlags
  ]
    .map(encodeURIComponent)
    .join(',')

  const socketUrl = `${wsProtocol}//${hostname}:${streamRelayWsPort}/api/stream/${rtspUsername}:${rtspPassword}@127.0.0.1/${panoramaStreamSuffix}?additionalFlags=${additionalFlags}`

  if (!player) {
    console.log('\x1b[32m%s\x1b[0m', 'Websocket mode')
    try {
      player = await loadPlayer({
        url: socketUrl,
        canvas: document.getElementById('canvas'),
        audio: false,
        videoBufferSize: 1024 * 1024 * 10,
        disconnectThreshold: 999999999999999,
        pauseWhenHidden: false,
        onSourceEstablished: () => {
          console.log('\x1b[32m%s\x1b[0m', 'Video source established')
        },
        onDisconnect: (p) => {
          console.log('\x1b[31m%s\x1b[0m', 'Video disconnected')
        },
        onPlay: (p) => {
          // console.log('\x1b[32m%s\x1b[0m', 'Video started playing')
        },
        onPause: (p) => {
          console.log('Video paused')
        }
      })
    } catch (error) {
      console.error('Error loading player:', error)
    }
  }
}

let mouseHandler = null // 鼠标事件处理器
const videoElementRef = ref(null)
const config = {
  ratioThreshold: 2, // 拖拽阈值比例 2%
  eventHandlers: {
    click: (params) => {
      message.info(`click`)
      handleEvent('left', params)
    },
    dblclick: () => {
      message.info(`dblclick`)
      fullScreen(videoElementRef.value)
    },
    drag: (params) => {
      message.info(`drag ${params.dragType}`)
      handleEvent('move', params)
    },
    wheel: (params) => {
      message.info(`wheel ${params.zoom}`)
      handleEvent('wheel', params)
    }
  }
}

onMounted(() => {
  mouseHandler = bindMouseEvents(videoElementRef.value, config)
})

onBeforeUnmount(() => {
  allowReconnect = false // 关闭重连

  if (eventRetryTimer) {
    clearTimeout(eventRetryTimer)
    eventRetryTimer = null
  }

  if (eventSocket) {
    eventSocket.close()
    eventSocket = null
  }

  if (mouseHandler) {
    mouseHandler.destroy()
    mouseHandler = null
  }

  if (player) {
    player.destroy()
  }
})
</script>

<style lang="less" scoped></style>
